/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 actor_model_impl.h

	$Header: /heroes4/actor_model_impl.h $

	$NoKeywords: $

 ************************************************************************/

#if !defined( ACTOR_MODEL_IMPL_H_INCLUDED )
#define ACTOR_MODEL_IMPL_H_INCLUDED

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "actor_sequence.h"
#include "actor_model.h"
#include "cached_ptr.h"

template < typename t_definition_arg >
t_actor_model< t_definition_arg >::t_actor_model( t_definition_ptr definition_ptr ) 
                                 : m_definition_ptr( definition_ptr )
{
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
template < typename t_definition_arg >
inline t_screen_point t_actor_model< t_definition_arg >::adjust_incoming( t_screen_point const & point ) const
{
	return point - m_definition_ptr->get_offset();
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
template < typename t_definition_arg >
inline t_screen_rect t_actor_model< t_definition_arg >::adjust_incoming( t_screen_rect const & rect ) const
{
	return rect - m_definition_ptr->get_offset();
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
template < typename t_definition_arg >
inline t_screen_point t_actor_model< t_definition_arg >::adjust_outgoing( t_screen_point const & point ) const
{
	return point + m_definition_ptr->get_offset();
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
template < typename t_definition_arg >
inline t_screen_rect t_actor_model< t_definition_arg >::adjust_outgoing( t_screen_rect const & rect ) const
{
	return rect + m_definition_ptr->get_offset();
}

// ----------------------------------------------------------------------
// get a sequence pointer for an action and direction
// ----------------------------------------------------------------------
template < typename t_definition_arg >
t_actor_sequence_ptr t_actor_model<t_definition_arg>::get_sequence_ptr( t_action_id action_id, 
												      t_direction direction ) const
{
	return get_sequence_cache( action_id, direction ).get();
}

// ------------------------------------------------------------------------------
// get a cache for a sequence given an action and a direction
// ------------------------------------------------------------------------------
template < typename t_definition_arg >
t_actor_sequence_cache t_actor_model< t_definition_arg >::get_sequence_cache(
													      t_action_id	action_id,
														  t_direction	direction ) const
{
	assert( action_id >= 0 && action_id < k_action_count );
	assert( direction >= 0 && direction < k_direction_count );

	if (!m_caches[action_id][direction].is_valid())
	{
		std::string const & sequence_name 
			= m_definition_ptr->get_sequence_name( action_id, direction );
		int					footprint_size = get_definition().get_footprint_size();
		t_screen_point      offset( 0, ( footprint_size * k_cell_height ) >> 1 );

		m_caches[action_id][direction] = get_cache( sequence_name, offset );
	}
	return m_caches[action_id][direction];
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
template < typename t_definition_arg > 
inline void t_actor_model< t_definition_arg >::draw_to(
	t_action_id				action_id,
	t_direction				direction,
	int						frame_num,
	t_screen_rect const &	source_rect,
	t_abstract_bitmap16 &	dest,
	t_screen_point const &	dest_point,
	int                     alpha ) const
{
	t_cached_ptr< t_actor_sequence > sequence_ptr = get_sequence_ptr( action_id, direction );
	if ( sequence_ptr.get() != 0 )
	{
		sequence_ptr->draw_to(
			frame_num,
			adjust_incoming( source_rect ),
			dest,
			adjust_outgoing( dest_point ), alpha );
	}
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
template < typename t_definition_arg >
inline void t_actor_model< t_definition_arg >::draw_to(
	t_action_id				action_id,
	t_direction				direction,
	int						frame_num,
	t_abstract_bitmap16 &	dest,
	t_screen_point const &	dest_point,
	int						alpha ) const
{
	t_cached_ptr< t_actor_sequence > sequence_ptr = get_sequence_ptr( action_id, direction );
	if ( sequence_ptr.get() != 0 )
		sequence_ptr->draw_to( frame_num, dest, adjust_outgoing( dest_point ), alpha );
}

template < typename t_definition_arg > 
inline void t_actor_model< t_definition_arg >::draw_to( t_bitmap_layer const*	image,
														t_screen_rect const&	source_rect,
														t_abstract_bitmap16&	dest,
														t_screen_point const&	dest_point,
														int                     alpha ) const
{
	image->draw_to( adjust_incoming( source_rect ), dest, adjust_outgoing( dest_point ), 
		            alpha );
}

template < typename t_definition_arg > 
inline void t_actor_model< t_definition_arg >::draw_to( t_bitmap_layer const*	image,
														t_abstract_bitmap16&	dest,
														t_screen_point const&	dest_point,
														int                     alpha  ) const
{
	image->draw_to( dest, adjust_outgoing( dest_point ), alpha );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
template < typename t_definition_arg >
inline void t_actor_model< t_definition_arg >::draw_shadow_to(
	t_action_id				action_id,
	t_direction				direction,
	int						frame_num,
	t_screen_rect const &	source_rect,
	t_abstract_bitmap16 &	dest,
	t_screen_point const &	dest_point,
	int						alpha ) const
{
	t_cached_ptr< t_actor_sequence > sequence_ptr = get_sequence_ptr( action_id, direction );
	if ( sequence_ptr.get() != 0 )
	{
		sequence_ptr->draw_shadow_to(
			frame_num,
			adjust_incoming( source_rect ),
			dest,
			adjust_outgoing( dest_point ), alpha );
	}
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
template < typename t_definition_arg >
inline void t_actor_model< t_definition_arg >::draw_shadow_to(
	t_action_id				action_id,
	t_direction				direction,
	int						frame_num,
	t_abstract_bitmap16 &	dest,
	t_screen_point const &	dest_point,
	int						alpha ) const
{
	t_cached_ptr< t_actor_sequence > sequence_ptr = get_sequence_ptr( action_id, direction );
	if ( sequence_ptr.get() != 0 )
		sequence_ptr->draw_shadow_to( frame_num, dest, adjust_outgoing( dest_point ), alpha );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
template < typename t_definition_arg >
typename t_actor_model< t_definition_arg >::t_definition const & t_actor_model< t_definition_arg >::get_definition() const
{
	return *m_definition_ptr;
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
template < typename t_definition_arg >
int t_actor_model< t_definition_arg >::get_footprint_size() const
{
	return m_definition_ptr->get_footprint_size();
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
template < typename t_definition_arg >
int t_actor_model< t_definition_arg >::get_frames_per_second( t_action_id action_id ) const
{
	return m_definition_ptr->get_frames_per_second( action_id );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
template < typename t_definition_arg >
int t_actor_model< t_definition_arg >::get_frame_count( t_action_id action_id, t_direction direction ) const
{
	t_actor_sequence_ptr sequence_ptr = get_sequence_ptr( action_id, direction );

	return sequence_ptr.get() != 0 ? sequence_ptr->get_frame_count() : 0;
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
template < typename t_definition_arg >
t_screen_point t_actor_model< t_definition_arg >::get_offset( t_action_id action_id, t_direction direction ) const
{
	t_actor_sequence_ptr sequence_ptr = get_sequence_ptr( action_id, direction );

	return	sequence_ptr.get() != 0
				? adjust_outgoing( sequence_ptr->get_offset() )
				: t_screen_point( 0, 0 );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
template < typename t_definition_arg >
t_screen_rect t_actor_model< t_definition_arg >::get_rect( t_action_id action_id, t_direction direction ) const
{
	t_cached_ptr< t_actor_sequence > sequence_ptr = get_sequence_ptr( action_id, direction );

	return	sequence_ptr.get() != 0
				? adjust_outgoing( sequence_ptr->get_rect() )
				: t_screen_rect( 0, 0, 0, 0 );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
template < typename t_definition_arg >
t_screen_rect t_actor_model< t_definition_arg >::get_rect( t_action_id action_id, t_direction direction, int frame_num ) const
{
	t_cached_ptr< t_actor_sequence > sequence_ptr = get_sequence_ptr( action_id, direction );

	return	sequence_ptr.get() != 0
				? adjust_outgoing( sequence_ptr->get_rect( frame_num ) )
				: t_screen_rect( 0, 0, 0, 0 );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
template < typename t_definition_arg >
t_screen_rect t_actor_model< t_definition_arg >::get_shadow_rect( t_action_id action_id, t_direction direction ) const
{
	t_cached_ptr< t_actor_sequence > sequence_ptr = get_sequence_ptr( action_id, direction );

	return	sequence_ptr.get() != 0
				? adjust_outgoing( sequence_ptr->get_shadow_rect() )
				: t_screen_rect( 0, 0, 0, 0 );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
template < typename t_definition_arg >
t_screen_rect t_actor_model< t_definition_arg >::get_shadow_rect( t_action_id action_id, t_direction direction, int frame_num ) const
{
	t_cached_ptr< t_actor_sequence > sequence_ptr = get_sequence_ptr( action_id, direction );

	return	sequence_ptr.get() != 0
				? adjust_outgoing( sequence_ptr->get_shadow_rect( frame_num ) )
				: t_screen_rect( 0, 0, 0, 0 );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
template < typename t_definition_arg >
bool t_actor_model< t_definition_arg >::has_action( t_action_id action_id, t_direction direction ) const
{
	return !m_definition_ptr->get_sequence_name( action_id, direction ).empty();
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
template < typename t_definition_arg >
bool t_actor_model< t_definition_arg >::hit_test(
	t_action_id				action_id,
	t_direction				direction,
	int						frame_num,
	t_screen_point const &	point ) const
{
	t_cached_ptr< t_actor_sequence > sequence_ptr = get_sequence_ptr( action_id, direction );

	return		sequence_ptr.get() != 0
			&&	sequence_ptr->hit_test( frame_num, adjust_incoming( point ) );
}

#endif // !defined( ACTOR_MODEL_IMPL_H_INCLUDED )
